/*
*    Phantom v2.0 GUI Automation Software
*    Copyright (C) 2009  John C. Van Arsdall (john@phantomtest.com)
*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU General Public License for more details.
*
*    You should have received a copy of the GNU General Public License along
*    with this program; if not, write to the Free Software Foundation, Inc.,
*    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
// Engine.cpp: implementation of the CEngine class.
//
//////////////////////////////////////////////////////////////////////

#include "ssi_engine.h"
#include "ssi_stream.h"

#include "ssi_datatypes.h"
#include "ssi_internals.h"
#include "ssi_functionset.h"

#include <math.h>
#include <stdio.h>
#include <stddef.h>
#include <time.h>
#include <direct.h>
#include "ssi_engine.h"
#include "ssi_functionapi.h"
#include "ssi_keywordapi.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////


// Hmmm... think I can get rid of all these...
#include "ssi_engine.h"
#include ".\ssi_engine.h"

// 1) Register functions same 
//    a) Remove Class Versus Global Flag
//    b) Keep return type
//    c) Keep parameter types
//    d) Add processing function
//        i) Do we need ghost class?
// 2) Upon function call, constructs parameter array
//    a) This is a pointer
//    b) Number of parameters is guaranteed by framework
// 3) Call processing function
//    a) The function pointer registered
//    b) Accepts parameter pointer, and number of params
//    c) Accepts function name
//		d) Sets the return data?

// This means we need a new structure/class that has:
// 1) Function Name/Identifier
// 2) Array of Parameters
// 3) A return Data
// 4) A function that sets returned data
// 5) A member indicating the number of parameters
// 6) A return status pointer
// All datas into and out of CDatas should be very large (longs?)
// 1) Then can type-cast... because Morgan loves you
// :-)

SSI_INT64 hello(SSI_INT64 Yo, SSI_INT64 Yo2, SSI_CHAR* str)
{
  //cout << "Why, hello there: " << Yo << ", " << Yo2 << " " << str << "\n";
  return 26;
}

SSI_INT64 accept_int(SSI_INT64 In){
	return In;
}

//void print_time()
//{
//#ifndef _WIN_CE_SCRIPT
//  char tmpbuf[128];
//  _strtime( tmpbuf );
//  printf( "time:\t\t\t\t%s\n", tmpbuf );
//#endif
//}

double print_clock(int i){
#ifndef _WIN_CE_SCRIPT
  static clock_t strt = 0;
  static clock_t stp = 0;
  static int finish_num = 0;
  double duration = 0; 

  if(i){
    finish_num = 0;
    strt = clock();
		return 0.0;
  }else{
    finish_num++;
    stp = clock();
    duration = (double)(stp - strt) / CLOCKS_PER_SEC;
		return duration;
  }
#else
	return 0.0;
#endif
}

void FunctionProcessor(SSI_FunctionAPI& theAPI){
	if(theAPI.m_FunctionName == "hello"){
		((CDataInt*)theAPI.GetReturnData())->Data = hello(((CDataInt*)theAPI[0])->Data, ((CDataInt*)theAPI[1])->Data, (char*)((CDataString*)theAPI[2])->Data.Str());
		return;
	}else if(theAPI.m_FunctionName == "print_time"){
		//print_time();
	}else if(theAPI.m_FunctionName == "clock"){
		CDataReal r;
		r.Data = print_clock(SSI_EXTRACT(CDataInt, 0));
		theAPI.SetReturnData(&r);
	}
}

void CEngine::SSI_FunctionProcessor(SSI_FunctionAPI& theAPI){
	if(theAPI.m_FunctionName == "who"){
    who(theAPI.m_Engine);
		return;
	}else if(theAPI.m_FunctionName == "what"){
		if(theAPI.GetNumParams() == 1){
			String package = SSI_EXTRACT(CDataString, 0);
			what(package);
		}else{
			what();
		}
		return;
	}else if(theAPI.m_FunctionName == "whatHELP"){
		String package = SSI_EXTRACT(CDataString, 0);
		String file = SSI_EXTRACT(CDataString, 1);
		whatHELP(package, file);
		return;
	}else if(theAPI.m_FunctionName == "members"){
		CVariable* theVar = theAPI[0];
		theVar->GetWorkingData().DisplayMembers(out);
		return;
	}else if(theAPI.m_FunctionName == "membersHELP"){
		CVariable* theVar = theAPI[0];
		String file = SSI_EXTRACT(CDataString, 1);
		theVar->GetWorkingData().DisplayMembersHELP(file);
		return;
	}else if(theAPI.m_FunctionName == "testadd"){
		SSI_INT64 Data = testadd(SSI_EXTRACT(CDataInt, 0),SSI_EXTRACT(CDataInt, 1));
		CDataInt a;
		a.Data = Data;
		theAPI.SetReturnData(&a);
	}else if(theAPI.m_FunctionName == "typeof"){
		CVariable* theVar = theAPI[0];
		CDataString a;
		a.Data = theVar->GetTypeString();
		theAPI.SetReturnData(&a);
	}else if(theAPI.m_FunctionName == "disp"){
		CVariable* theVar = theAPI[0];
		CData* theData;
		SSI_INT64 ctr;
		SSI_INT64 num_elems = theVar->GetNumElems();
		for(ctr = 0; ctr < num_elems; ctr++){
			theData = theVar->Get(ctr);
			if(!theData){
				theData = CData::NewData(theVar->GetType());
				disp(theData);
				delete theData;
			}else{
				disp(theData);
			}
		}
    return;
  }else if(theAPI.m_FunctionName == "test_cwd"){
    CDataString s;
		s.Data = Environment->GetCWD();
		theAPI.SetReturnData(&s);
  }else if(theAPI.m_FunctionName == "test_cd"){
    String s = SSI_EXTRACT(CDataString, 0);
    theAPI.m_ReturnStatus = Environment->SetCWD(s);
    
    CDataInt a;
		a.Data = 1;
		theAPI.SetReturnData(&a);
  }else if(theAPI.m_FunctionName == "test_getpath"){
    String s = SSI_EXTRACT(CDataString, 0);
    
    CDataString d;
    theAPI.m_ReturnStatus = Environment->GetAbsolutePath(s, d.Data);
		theAPI.SetReturnData(&d);
  }
  else if(theAPI.m_FunctionName == "exists"){
    String s = SSI_EXTRACT(CDataString, 0);
    CDataBool d;
    CVariable Var;
    Var.Name = s;
    // This will not work, because it only detects variables
    // that are a part of the registrar engine, not a function engine.
    //d.Data = GetVariable(Var);

    d.Data = theAPI.m_Engine->GetVariable(Var);
		theAPI.SetReturnData(&d);
  }
}


CEngine::CEngine(SSI_BOOL bAddLocalFunctions):
parEngine(SSI_NULL)
, CallingClass(SSI_NULL)
, Environment(NULL)
, log(NULL)
, IsLoopEngine(SSI_NULL)
, FuncSet(NULL)
{
  Interpreter = SSI_NULL;

  CFunctionDecl aNewFunc;
  FuncSet = new CFunctionSet();

	FuncSet->SetPackageName("Standard");

	UserPackageName = "User Defined";

  Environment = new CEnvironment();
  char cdir[MAX_SSI_CHARS];
  if(getcwd(cdir, MAX_SSI_CHARS) != SSI_NULL){
    Environment->SetCWD(String(cdir));
  }  

  if(bAddLocalFunctions){

  #ifdef _DEBUG
    aNewFunc.Name = "hello";
	  aNewFunc.addType(CDataInt::GetRegisteredType());
    aNewFunc.addType(CDataInt::GetRegisteredType());
    aNewFunc.addType(CDataString::GetRegisteredType());
    aNewFunc.ReturnType = CDataInt::GetRegisteredType();
	  aNewFunc.SetProcessor((ProcessingFunctionPtr)FunctionProcessor);
    AddFunction(aNewFunc);
    
    aNewFunc.Clear();
    aNewFunc.Name = "accept_int";
    aNewFunc.addType(CDataInt::GetRegisteredType());
	  aNewFunc.ReturnType = CDataInt::GetRegisteredType();
    aNewFunc.SetProcessor((ProcessingFunctionPtr)FunctionProcessor);
	  AddFunction(aNewFunc);

	  aNewFunc.Clear();
    aNewFunc.Name = "testadd";
    aNewFunc.addType(CDataInt::GetRegisteredType());
    aNewFunc.addType(CDataInt::GetRegisteredType());
    aNewFunc.ReturnType = CDataInt::GetRegisteredType();
    aNewFunc.SetProcessor(this);
    AddFunction(aNewFunc);
    
    aNewFunc.Clear();
    aNewFunc.Name = "print";
    aNewFunc.addType(CDataString::GetRegisteredType());
	  aNewFunc.SetProcessor(this);
    AddFunction(aNewFunc);

    aNewFunc.Clear();
    aNewFunc.Name = "getstring";
    aNewFunc.ReturnType = CDataString::GetRegisteredType();
    aNewFunc.Flags = RETURN_CLASS;
    aNewFunc.SetProcessor(this);
    AddFunction(aNewFunc);

    aNewFunc.Clear();
    aNewFunc.Name = "getidentity";
    aNewFunc.ReturnType = CDataInt::GetRegisteredType();
    aNewFunc.Flags = RETURN_ARRAY;
    aNewFunc.SetProcessor(this);
    AddFunction(aNewFunc);

    aNewFunc.Clear();
    aNewFunc.Name = "test_cwd";
    aNewFunc.ReturnType = CDataString::GetRegisteredType();
    aNewFunc.SetProcessor(this);
    AddFunction(aNewFunc);

    aNewFunc.Clear();
    aNewFunc.Name = "test_cd";
    aNewFunc.ReturnType = CDataInt::GetRegisteredType();
    aNewFunc.addType(CDataString::GetRegisteredType());
    aNewFunc.SetProcessor(this);
    AddFunction(aNewFunc);

    aNewFunc.Clear();
    aNewFunc.Name = "test_getpath";
    aNewFunc.ReturnType = CDataString::GetRegisteredType();
    aNewFunc.addType(CDataString::GetRegisteredType());
    aNewFunc.SetProcessor(this);
    AddFunction(aNewFunc);

	  aNewFunc.Clear();
    aNewFunc.Name = "whatHELP";
	  aNewFunc.addType(CDataString::GetRegisteredType());
	  aNewFunc.addType(CDataString::GetRegisteredType());
	  aNewFunc.SetProcessor(this);
    AddFunction(aNewFunc);

    aNewFunc.Clear();
    aNewFunc.Name = "membersHELP";
    aNewFunc.addType(dANY);
	  aNewFunc.addType(CDataString::GetRegisteredType());
    aNewFunc.SetProcessor(this);
    AddFunction(aNewFunc);

  #endif

    //aNewFunc.Clear();
    //aNewFunc.Name = "time";
	  //aNewFunc.SetProcessor((ProcessingFunctionPtr)FunctionProcessor);
    //AddFunction(aNewFunc);

    aNewFunc.Clear();
    aNewFunc.Name = "clock";
    aNewFunc.addType(CDataInt::GetRegisteredType());
	  aNewFunc.ReturnType = CDataReal::GetRegisteredType();
    aNewFunc.SetProcessor((ProcessingFunctionPtr)FunctionProcessor);
    AddFunction(aNewFunc);

	  //////////////////////////////////////////////
	  //////////////////////////////////////////////

    aNewFunc.Clear();
    aNewFunc.Name = "who";
	  aNewFunc.SetProcessor(this);
    AddFunction(aNewFunc);

	  aNewFunc.Clear();
    aNewFunc.Name = "what";
	  aNewFunc.SetProcessor(this);
    AddFunction(aNewFunc);

	  aNewFunc.Clear();
    aNewFunc.Name = "what";
	  aNewFunc.addType(CDataString::GetRegisteredType());
	  aNewFunc.SetProcessor(this);
    AddFunction(aNewFunc);

    aNewFunc.Clear();
    aNewFunc.Name = "typeof";
    aNewFunc.addType(dANY);
    aNewFunc.ReturnType = CDataString::GetRegisteredType();
    aNewFunc.SetProcessor(this);
    AddFunction(aNewFunc);

    aNewFunc.Clear();
    aNewFunc.Name = "members";
    aNewFunc.addType(dANY);
    aNewFunc.SetProcessor(this);
    AddFunction(aNewFunc);

	  aNewFunc.Clear();
    aNewFunc.Name = "disp";
    aNewFunc.addType(dANY);
    aNewFunc.SetProcessor(this);
    AddFunction(aNewFunc);

	  aNewFunc.Clear();
    aNewFunc.Name = "exists";
    aNewFunc.addType(CDataString::GetRegisteredType());
    aNewFunc.ReturnType = CDataBool::GetRegisteredType();
    aNewFunc.SetProcessor(this);
    AddFunction(aNewFunc);
  }
}

CEngine::~CEngine()
{
  if(!parEngine){
    delete Environment;
    Environment = SSI_NULL;

    delete FuncSet;
    FuncSet = SSI_NULL;

    if(log){
      delete log;  // this closes it, too
      log = SSI_NULL;
    }
  }

}

void CEngine::SetInterpreter(CInterpreter *Interp)
{
  Interpreter = Interp;
}


SSI_BOOL CEngine::CheckName(const String& sName)
{
  const char* s = sName.Str();
  if(strlen(s) == 0){
    return 0;
  }
  for(int i = 0; i < strlen(s); i++){
    char c = s[i];
    if(!isalnum(c)){
      if(c != '_'){
        return 0;
      }
    }
  }
  return 1;
}


// Need to have a determined dimension node
CStatus CEngine::AddNewVariable(SSI_INT64 Type, const String& Name, String InitialValue)
{
  CVariable aVar;

  InitialValue.Trim();
  

  aVar.Name = Name;
  aVar.Name.Trim();
  if(!CheckName(aVar.Name)){
    MAKEERR1(ERR01126, aVar.Name.Str());
  }

  aVar.SetType(Type);
  
  aVar.SetData(InitialValue, SSI_NULL);
  //VarNode* aNode = Vars.find(aVar);
  // Cant use this, as will not find variables of different types
	//VarItor Itor = find(Vars.begin(), Vars.end(), aVar);

  VarItor Itor = Vars.begin();
  while(Itor != Vars.end()){
    if((*Itor).Name == aVar.Name){
      MAKEERR1(ERR02026, aVar.Name.Str());
    }
    Itor++;
  }

  //if(Itor != Vars.end())
  //{
  //  RetMess.Format("Variable '%s' already exists", Name.Str());
  //  RetStat.Set(sERROR, RetMess);
  //  return RetStat;
  //}
	Vars.push_back(aVar);
  //aNode = Vars.push(aVar);
  //if(!aNode)
  //{
  //  // Come up with some other error
  //  return CStatus(sERROR, ERR02000);
  //}
  return STAT_SUCCESS;
}

CStatus CEngine::AddVariable(CVariable newVar)
{
  //VarNode* aNode = Vars.find(newVar);

  VarItor Itor = Vars.begin();
  while(Itor != Vars.end()){
    if((*Itor).Name == newVar.Name){
      MAKEERR1(ERR02027, newVar.Name.Str());
    }
    Itor++;
  }


  /*
  CANNOT USE, WILL ALLOW VARIABLES OF DIFFERENT TYPES
	VarItor Itor = find(Vars.begin(), Vars.end(), newVar);
	if(Itor != Vars.end())
  {
    RetMess.Format("Variable '%s' already exists", newVar.Name.Str());
    RetStat.Set(sERROR, RetMess);
    return RetStat;
  }
  */

	Vars.push_back(newVar);
  //aNode = Vars.push(newVar);
  //if(!aNode)
  //{
  //  // Come up with some other error
  //  return CStatus(sERROR, ERR02001);;
  //}
  return STAT_SUCCESS;
}

CStatus CEngine::CopyVariable(CVariable& inVar, const String& newName)
{
  CVariable aVar;
  aVar.CopyVariable(inVar);

  aVar.Name = newName;
  //VarNode* aNode = Vars.find(aVar);


  VarItor Itor = Vars.begin();
  while(Itor != Vars.end()){
    if((*Itor).Name == aVar.Name){
      MAKEERR1(ERR02028, aVar.Name.Str());
    }
    Itor++;
  }

  /*
	VarItor Itor = find(Vars.begin(), Vars.end(), aVar);
	if(Itor != Vars.end())
  {
    RetMess.Format("Variable '%s' already exists", newName.Str());
    RetStat.Set(sERROR, RetMess);
    return RetStat;
  }
  */
	Vars.push_back(aVar);
  //aNode = Vars.push(aVar);
  //if(!aNode)
  //{
  //  // Come up with some other error
	//	return CStatus(sERROR, ERR02002);
  //}
  return STAT_SUCCESS;
}

void CEngine::who(CEngine* In, SSI_BOOL bLabel)
{
  if(In == 0){
    return;
  }
  In->Vars.sort();
  VarItor Top = In->Vars.begin();
	SSI_CHAR StrBuff[68];
  CVariable vTemp;
  CSSI_CHAR* fmt = "%-13.12s%-12.11s%-42.42s";
  if(bLabel){
	  sprintf(StrBuff, fmt, "Variable", "Type", "Value");
    out->Disp(StrBuff, 1);
    out->Disp("------------------------------------------------------------------------------", 1);
  }
	while(Top != In->Vars.end())
  {
    String Mess;
    String Temp;
    //Top->Get(vTemp);
		vTemp = *Top;

    Temp = Mess;
    String sName = vTemp.Name;
    if(bLabel == 0){
      // Dont want this anymore because loops
      // sName = String("* ") + sName;
    }
    if(vTemp.GetNumElems() > 1)
    {
			Mess.Format("%s[%d]", (CSSI_CHAR*)vTemp.GetTypeString(), vTemp.GetNumElems());
			sprintf(StrBuff, fmt, (CSSI_CHAR*)sName.Str(), (CSSI_CHAR*)Mess, (CSSI_CHAR*)vTemp.Get().GetString());
      Mess.Format("%s", (CSSI_CHAR*)StrBuff);
    }
    else
    {
		  sprintf(StrBuff, fmt, (CSSI_CHAR*)sName.Str(), (CSSI_CHAR*)vTemp.GetTypeString(), (CSSI_CHAR*)vTemp.Get().GetString());
			Mess.Format("%s", (CSSI_CHAR*)StrBuff);
    }
		out->Disp(Mess.Str(), 1);
    //Top = Top->next();
		++Top;
  }

  if(In->parEngine){
    CEngine* TempEngine = In->parEngine;

    if(In->IsLoopEngine){
      // In this case, will handle all parents
      // until no longer loop
      who(TempEngine, 0);
      return;
    }

    // Want to do this so can only see locals and globals, not
    // calling function locals
    while(TempEngine->parEngine){
      TempEngine = TempEngine->parEngine;
    }
    //out->Disp("", 1);
    //out->Disp("Global Variables", 1);
    who(TempEngine, 0);
    // dont want this anymore because loops
    // out->Disp("  * global variable", 1);
  }
}

// Should really do something with this...
//void CEngine::SetIO(COutput anOut, COutput anErr, COutput anIn)
//{
//}

CStatus CEngine::DoFunction(CFunction &aFunc)
{

  //String RetMess;
  
  //String* s = SSI_NULL;

  //SSI_INT64 ctr = 0;
 
  //const SSI_PTR* data_ptr = SSI_NULL;

	//API---
  //SSI_FunctionAPI theAPI;
  //INT32 Real_Type = CDataReal::GetRegisteredType();
	
 
  ///  BE SURE TO SET UP A FUNCTION CHECKER SO FUNCTIONS CAN BE
  ///  EASILY ADDED TO CENGINE AND NONE OF THIS CRAP NEEDS TO BE 
  ///  DONE


  //aDecl.Name = aFunc.Name;

	///// KEEP THIS!!!!
//	if(CallingClass){

	//	return CallingClass->CallFunction(aFunc);

	///// KEEP THIS!!!!!

		// Calling Class Find Function
		/*
		if(CallingClass->MyEngine->FindFunctionDecl(aDecl))
		{
		}
		else
		{
			RetMess.Format("Function '%s' is not a member of '%s'", aDecl.Name.Str(), CallingClass->DataName.Str());
			RetStat.Set(sERROR, RetMess);
			return RetStat;
		}
		NumParams = aDecl.getNumParams();

		if(aFunc.GetNumParams() != NumParams)
		{
			RetMess.Format("Function '%s' requires %d parameters", aDecl.Name.Str(), NumParams);
			RetStat.Set(sERROR, RetMess);
			return RetStat;
		}
		*/

		//if(aDecl.Access > ACCESS_PUBLIC){
  	//}

		/*
	  if(aDecl.Flags & USER_FUNCTION_TYPE){
			CEngine* oldEng = this;
			Interpreter->SetEngine(CallingClass->MyEngine);
			RetStat = Interpreter->ProcessUserFunction(aFunc, aDecl);
			Interpreter->SetEngine(oldEng);
			return RetStat;
		}else{
			CallingClass->MyEngine->CallingClass = SSI_NULL;
			RetStat = CallingClass->MyEngine->DoFunction(aFunc);
			CallingClass->MyEngine->CallingClass = CallingClass;
			return RetStat;
		}
		*/

  //}

	/*
  if(FindFunctionDecl(aDecl))
  {
	}
	else
	{
		RetMess.Format("Function '%s' not found", aDecl.Name.Str());
		RetStat.Set(sERROR, RetMess);
		return RetStat;
	}

	NumParams = aDecl.getNumParams();
	if(aFunc.GetNumParams() != NumParams)
	{
		RetMess.Format("Function '%s' requires %d parameters", aDecl.Name.Str(), NumParams);
		RetStat.Set(sERROR, RetMess);
		return RetStat;
	}

  if(aDecl.Flags & USER_FUNCTION_TYPE){
    return Interpreter->ProcessUserFunction(aFunc, aDecl);
  }

	//Push the variables on
	NumParams = aFunc.GetNumParams();

	//API---
	theAPI.Parameters = new CVariable*[NumParams+1];
	theAPI.m_FunctionName = aDecl.Name;

	CVariable* Vars = new CVariable[NumParams+1];

	for(ctr = 0; ctr < NumParams; ctr++){
    Vars[ctr] = aFunc.GetVariable(ctr);
		if((Vars[ctr].GetType() != aDecl.getParamType(ctr)) && (aDecl.getParamType(ctr) != dANY))
    {
      RetMess.Format("'%s' parameter %d requires type %s; type %s found", aDecl.Name.Str(), ctr, CInterpreter::GetVariableString(aDecl.getParamType(ctr-1)).Str(), Vars[ctr].GetTypeString().Str());
      RetStat.Set(sERROR, RetMess);
      return RetStat;
    }
		theAPI.Parameters[ctr] = &Vars[ctr];
	}
	theAPI.Parameters[ctr] = 0;
	
	FuncRetVar = aFunc.GetReturn();
    FuncRetVar.SetType(aDecl.ReturnType);
	theAPI.RetVar = &FuncRetVar;
	*/

	if(CallingClass){
		return CallingClass->CallFunction(aFunc);
	}


	CStatus RetStat;
  CFunctionDecl aDecl;
	SSI_FunctionAPI theAPI;

	theAPI.m_Engine = this;

	RetStat = FuncSet->CheckFunction(aFunc, theAPI, aDecl);
	if(!RetStat) return RetStat;

  if(aDecl.Flags & USER_FUNCTION_TYPE){

    int test;
    unsigned int check_stack = (unsigned int)&test;
    unsigned int check_diff; //= Environment->GetStackAddress() - (unsigned int)(&check_stack); 
    unsigned int last_stack = aFunc.GetStack();
    if(last_stack > 0){
      check_diff = last_stack - check_stack;
      if(check_diff > (check_stack - Environment->GetStackEnd())){
        MAKEERR0(ERR01004);
      }
    }
    aFunc.SetStack(check_stack);    
    RetStat = Interpreter->ProcessUserFunction(aFunc, aDecl);
    aFunc.SetStack(0);
    if(!RetStat){
      return RetStat;
    }
    if(aFunc.GetReturn().GetType() != aDecl.ReturnType){
      MAKEERR2(ERR03333, aDecl.Name.Str(), CInterpreter::GetVariableString(aDecl.ReturnType).Str());
    }
    return RetStat;
  }

	
	if(aDecl.FunctionType == GLOBAL_FUNCTION_TYPE){
		aDecl.theProcessor(theAPI);
	}else{
		aDecl.objectPtr->SSI_FunctionProcessor(theAPI);
	}

  if(theAPI.GetReturnData()){
    if(theAPI.GetReturnData()->GetType() != aDecl.ReturnType){
      if(aDecl.ReturnType != dANY){  // If any data type is returned, then just pass it along
      MAKEERR2(ERR03334, aDecl.Name.Str(), CInterpreter::GetVariableString(aDecl.ReturnType).Str());
    }
  }
  }

	aFunc.SetReturn(theAPI.RetVar);
	return theAPI.m_ReturnStatus;
}

CStatus CEngine::ProcessInternalFunction(CFunction& aFunc, CFunctionDecl& aDecl)
{
	// Why is this function here?
	return STAT_ERR;   // Internal Error
}




SSI_BOOL CEngine::IsFunction(const String &fName)
{
  CFunctionDecl aDecl;
  aDecl.Name = fName;
  aDecl.Name.Trim();
	return FuncSet->IsFunction(aDecl);
  //if(FindFunctionDecl(aDecl)) return 1;
  //return 0;
}

SSI_BOOL CEngine::GetVariable(CVariable &Var, SSI_BOOL bLocalOnly)
{
  //VarNode* aNode = SSI_NULL;
	VarItor Itor;

	DimNode dNode = SSI_NULL;
  DimNode destroyNode = SSI_NULL;
	String Name = Var.Name;
	String Temp;

  // Dont want to destroy this node if it exists...
	dNode = Var.GetWorkingNode(); 
	if(!dNode){
		if(destroyNode = Interpreter->GetArrayDimensions(Name, Temp)){
      dNode = destroyNode;
			if(destroyNode != ERROR_NODE){
				Var.Name = Temp;
			}
		}
  }else{
    // Need to do this, so when Var.SetWorkingNode, does
    // not destroy the dNode that it is set to (later)
    destroyNode = copy_array_node(dNode);
    dNode = destroyNode;
  }

	if(dNode == ERROR_NODE){
    if(destroyNode){
      del_array_node(destroyNode);
    }
		return 0;
	}

	Itor = find(Vars.begin(), Vars.end(), Var);
	//aNode = Vars.find(Var);
  
	if(Itor != Vars.end())
  {
		Var = *Itor;
		if(dNode){
      // Would destroy dNode if it were not copied.
			Var.SetWorkingNode(dNode);
		}else{
			Var.SetWorkingNode(SSI_NULL);
		}

    // Dont delete here, as the variable will
    // destroy it if need be, because it was set.
    // Original dNode was destroyed in the SetWorkingNode
    // statement.  This is because we are using the same
    // variable.
    //if(destroyNode){
    //  del_array_node(destroyNode);
    //}

    return 1;
	}else{
    dNode = SSI_NULL;
	}

  if(destroyNode){
    del_array_node(destroyNode);
  }

	Var.Name = Name;
	
	// OK, fool... wait.  Should CallingClass's variables be found this way as well?  Not just super?
	// Yeah, maybe so... this can remove the necessity of synchronize data?
	// No... this has to be done here, too, since operators and functions will need to get the variable via the engine
	if(CallingClass){
		//if(CallingClass->GetVariable(Var, this, ACCESS_PRIVATE)){
		if(CallingClass->GetVariable(Var)){
			return 1;
		}
	}

	if(parEngine && !bLocalOnly){
    CEngine* TempEngine = parEngine;

    // If it is a loop engine, want it to find the variable
    if(IsLoopEngine){
      return TempEngine->GetVariable(Var);
    }

    // Want to do this so can only see locals and globals, not
    // calling function locals
    while(TempEngine->parEngine){
      TempEngine = TempEngine->parEngine;
    }
    return TempEngine->GetVariable(Var);
  }
	return 0;
}

CStatus CEngine::Evaluate(CStatement& aStat)
{
	
  CStatus Ret = STAT_OK;
  switch(aStat.Type)
  {
  case(S_FUNCTION):
    Ret = DoFunction(aStat.aFunction);

		// THIS HERE IS A PROBLEM IF A FUNCTION RETURN FROM A CLASS... CHECK OUT DOFUNCTION!

		aStat.RetVar = aStat.aFunction.GetReturn();
    return Ret;
  case(S_OPERATOR):
    Ret = DoOperator(aStat.anOperator);
    if(!Ret) return Ret;
		aStat.RetVar = aStat.anOperator.Result;
    break;
	case(S_GETVAR):
    {
		  if(GetVariable(aStat.RetVar)){
			  return STAT_SUCCESS;
		  }
		  // Variable not found
		  // return STAT_FAIL;  // I think this should be either pass/fail
		  //return CStatus(sERROR, ERR02003);;
      CScriptLine aLine;
      
      
      //{MAKEERR1(ERR02020, aStat.Line.Str());}
      // This was made this way for errors in if statements
      {MAKEERR1(ERR02020, aStat.RetVar.Name.Str());}
      //if(aStat.Interp->GetCurrLine(aLine)){
      //  {MAKEERR1(ERR02020, aLine.Line.Str());}
      //}else{
		  //  {MAKEERR1(ERR02020, aStat.RetVar.Name.Str());}
      //}
    }
    
  default:
    return STAT_ERR;  // Internal Error
  }
  return STAT_OK;
}

CStatus CEngine::SetVariable(CVariable &aVar)
{
  //VarNode* aNode = SSI_NULL;
	VarItor Itor;
  CStatus Ret;
  CVariable Temp = aVar;
	if(!GetVariable(Temp)){
		MAKEERR1(ERR02013, aVar.Name.Str());
	}
	Itor = find(Vars.begin(), Vars.end(), Temp);
  //aNode = Vars.find(Temp);

	if(Itor == Vars.end()){
		if(CallingClass){
			// *** NEED TO ORDER THIS PROPERLY!!!!
			//if(CallingClass->SetVariable(aVar, this, ACCESS_PRIVATE)){
			if(CallingClass->SetVariable(aVar)){
				return STAT_SUCCESS;
			}
		}
    if(parEngine){
      return parEngine->SetVariable(aVar);
    }
  }

	*Itor = aVar;
  //aNode->Set(aVar);
  return STAT_SUCCESS;
}

CStatus CEngine::SetVariable(VarTree &VarSet, CVariable &InVar)
{
	VarItor Itor = find(VarSet.begin(), VarSet.end(), InVar);
	if(Itor == VarSet.end()) return STAT_ERR; // Internal Error
	//VarNode* aNode = VarSet.find(InVar);
	//if(!aNode) return STAT_ERR;   // Internal Error
	--Itor;
	if(Itor == VarSet.end()) return STAT_ERR;
	//VarNode* Prev = aNode->prev();
	//if(!Prev) return STAT_ERR;    // Internal Error

	CVariable aVar = *Itor;
	//Prev->Get(aVar);
	return aVar.GetWorkingData().SetVariable(InVar);

	/*
  VarNode* aNode = SSI_NULL;
  CStatus Ret;
  CVariable Temp = aVar;
	if(!GetVariable(Temp)){
		MAKEERR1(ERR02013, aVar.Name.Str());
	}
  aNode = Vars.find(Temp);

  if(!aNode){
		if(CallingClass){
			// *** NEED TO ORDER THIS PROPERLY!!!!
			if(CallingClass->SetVariable(aVar, this, ACCESS_PRIVATE)){
				return STAT_SUCCESS;
			}
		}
    if(parEngine){
      return parEngine->SetVariable(aVar);
    }
  }

  aNode->Set(aVar);
  return STAT_SUCCESS;
	*/
}





SSI_INT64 CEngine::test()
{
  return 26;
}

void CEngine::print(const SSI_CHAR* S)
{
	out->Disp(S);
}

SSI_INT64 CEngine::testadd(SSI_INT64 i1, SSI_INT64 i2)
{
  return i1 + i2;
}


CStatus CEngine::AddFunction(CFunctionDecl& aFunc)
{
  if(!CheckName(aFunc.Name)){
    MAKEERR1(ERR01127, aFunc.Name.Str());
  }

	return FuncSet->AddFunction(aFunc);
}

CStatus CEngine::AddClass(CClassDecl aClass)
{
	CClassDecl Temp = aClass;
	CDataClass* cSuper = SSI_NULL;
	CDataClass* cNewClass = SSI_NULL;
	CClassDecl cSuperDecl;
	ClassNode* aClassPtr = SSI_NULL;

	// Maybe get rid of this if nested classes are desired
	if(parEngine){
    return parEngine->AddClass(Temp);
  }

	if(aClass.SuperName.GetLength() > 0){
		cSuperDecl.ClassName = aClass.SuperName;
		if(!(aClassPtr = Classes.find(cSuperDecl))){
      String Mess;
			Mess.Format("Superclass '%s' undefined", aClass.SuperName.Str());
      return CStatus(sERROR, Mess);
		}else{
			//aClassPtr->Get(cSuper);
			// What to do with supers here?
			// Should they be decls?
			aClassPtr->Get(cSuperDecl);
			cSuper = cSuperDecl.RepClass;
		}
	}

	if(aClassPtr = Classes.find(Temp)){
		return STAT_OK;
	}else{

		// This will register it
		// 1 SSI_DISPLAY_FILE_NEW
		cNewClass = new CDataClass(Temp.ClassName, cSuper);
		// The decl will delete the class itself?
		// We will see...
		Temp.RepClass = cNewClass;

		aClassPtr = Classes.push(Temp);
		if(aClassPtr) return STAT_OK;
	}
  return CStatus(sERROR, ERR02006);;
}

CStatus CEngine::FindFunctionDecl(CFunctionDecl &aFunc)
{

	//if(FuncSet.FindFunctionDecl(aFunc, 0)){
	//}else{
	//	return STAT_FAIL;
	//}
	return STAT_FAIL; //FuncSet.FindFunctionDecl(aFunc);
	//return FuncSet.FindFunctionDecl(aFunc);
  //FuncNode* aFuncPtr;
  //if(aFuncPtr = Funcs.find(aFunc))
  //{
  //  aFuncPtr->Get(aFunc);

  //  return STAT_OK;
  //}

	// CALL FUNCSET
	// THEN DO BELOW IF FAILURE

/*
  if(parEngine){
		if(parEngine->FindFunctionDecl(aFunc)){
			if(aFunc.Access > ACCESS_PROTECTED){
				// Cannot access a private member...
				// Will this work here?
				// No... cant do, because private members will fail
				//return STAT_ERR;
			//}else{
				return STAT_OK;
			}
		}else{
			return STAT_ERR;
		}
  }

  return STAT_FAIL;
	*/
}


CStatus CEngine::FindClassDecl(CClassDecl &aClass)
{
	// Change this if want nested classes
  if(parEngine){
    return parEngine->FindClassDecl(aClass);
  }

  ClassNode* aClassPtr;
  if(aClassPtr = Classes.find(aClass))
  {
    aClassPtr->Get(aClass);
    return STAT_OK;
  }
	String sErr;
	sErr.Format(ERR02007, aClass.ClassName.Str());
  return CStatus(sERROR, sErr);
}


CStatus CEngine::equal(COperator &Eq)
{
	//FOR EQUAL, NEED TO SET BOTH LEFT AND RESULT

	OpInfoT anInfo;
	anInfo.Size = sizeof(anInfo);
	anInfo.anEngine = this;

  if(Eq.Type == oEQUAL)
  {
		// Must do this to verify that data exists, and get the type if unknown

		// HAVE IT FIND THE VARIABLES BEFORE HERE
		/*
    CVariable FindVar = Eq.Left_Var;
		if(!GetVariable(FindVar)){
			MAKEERR1(ERR02008, FindVar.Name.Str());
			//String sErr;
			//sErr.Format(ERR02007, FindVar.Name.Str());
			//return CStatus(sERROR, sErr);
		}
		*/
		// Be sure to set the type if it was unknown
		// Cant just do a set type
		Eq.Result = Eq.Left_Var;
    Eq.Result.SetType(Eq.Left_Var.GetType());

		if(Eq.Left_Var.GetWorkingNode() || Eq.Right_Var.GetWorkingNode() /*|| (Eq.LeftOvers.GetLength() > 0)*/)
    {
			CData* lData = &Eq.Left_Var.GetWorkingData();
			CData* rData = &Eq.Right_Var.GetWorkingData();
			if(!rData) return CStatus(sERROR, ERR02009);;

			Eq.Result.SetType(lData->GetType());
			if(!lData->operator_equal_internal(rData, &anInfo)){
					return STAT_FAIL;
			}
		}

		// No class stuff in here
		else
		{
			DimNode aDim;
			SSI_BOOL bEOL = 0;
			CData* lData = SSI_NULL;
			CData* rData = SSI_NULL;
			CData* aData = SSI_NULL;

			Eq.Result.ResetArray(SSI_NULL); // Deletes old, Creates a new Array
			Eq.Left_Var.ResetArray(SSI_NULL);
			SSI_BOOL bReset = 1;

      if(Eq.Right_Var.GetNumElems() == 0){
        return STAT_FAIL;
      }

			while(!bEOL){
				rData = Eq.Right_Var.GetNextData(aDim, bEOL, bReset);
				bReset = 0;
				if(rData){
					aData = Eq.Left_Var.NewData();
					if(!aData) return CStatus(sERROR, ERR02010);;
					if(!aData->operator_equal_internal(rData, &anInfo)){
						delete aData;
						return STAT_FAIL;
					}
					Eq.Left_Var.SetData(aData, aDim);
					delete aData;
				}
			}
		}
	}else{
		return CStatus(sERROR, ERR02015);
	}

	Eq.Result.CopyAllData(Eq.Left_Var);

	// THIS NEEDS TO BE DONE LATER, BECAUSE IF A CLASS HAS IT DONT WANT IT HERE?
	//RetStat = SetVariable(Eq.Result);
	//if(!RetStat) return RetStat;    
	return STAT_SUCCESS;
}

CStatus CEngine::DoOperator(COperator &anOp)
{
	CStatus Ret;


	if(anOp.Type == oEQUAL_EQUAL){
    Ret = compare(anOp);
		return Ret;
	}else if(anOp.Type == oNOT_EQUAL){
    Ret = compare(anOp);
		return Ret;
	}else if(anOp.Type & oLESS){
    Ret = compare(anOp);
		return Ret;
	}else if(anOp.Type & oGREATER){
    Ret = compare(anOp);
		return Ret;
	}
	

  switch(anOp.Type)
  {
  case (oEQUAL):
    {
      Ret = equal(anOp);
			break;
    }
  case (oPLUS):
    {
      Ret = plus(anOp);
			break;
    }
  case (oMULTIPLY):
    {
      Ret = multiply(anOp);
			break;
    }
  case (oPOWER):
    {
      Ret = power(anOp);
			break;
    }
  case (oDIVIDE):
    {
      Ret = divide(anOp);
			break;
    }
  case (oMINUS):
    {
      Ret = subtract(anOp);
			break;
    }
  case (oBIT_AND):
    {
      Ret = bit_and(anOp);
			break;
    }
  case (oBIT_OR):
    {
      Ret = bit_or(anOp);
			break;
    }
  case (oLOG_AND):
    {
      Ret = log_and(anOp);
			break;
    }
  case (oLOG_OR):
    {
      Ret = log_or(anOp);
			break;
    }
  case (oINCREMENT):
    {
      Ret = increment(anOp);
			break;
    }
  case (oDECREMENT):
    {
      Ret = decrement(anOp);
			break;
    }
  case (oMODULOUS):
    {      
      Ret = modulous(anOp);
			break;
    }

  default: return CStatus(sERROR, ERR02012);
  }
	if(Ret == STAT_ERR) return Ret;
	if(Ret == STAT_FAIL){
		String sErr;
		sErr.Format(ERR02014, anOp.TypeToStr(anOp.Type).Str(), anOp.Left_Var.GetTypeString().Str(), anOp.Right_Var.GetTypeString().Str());
		return CStatus(sERROR, sErr);
	}
	return STAT_OK;
}

CStatus CEngine::plus(COperator &anOp)
{
	CData* lData = &anOp.Left_Var.GetWorkingData();
  CData* rData = &anOp.Right_Var.GetWorkingData();
	 
	anOp.Result.SetType(lData->GetType());
	CData* res = &anOp.Result.GetWorkingData();

	*res = *lData;
	if(!res->operator_plus(rData)){
		return STAT_FAIL;
	}
	return STAT_SUCCESS;
}

String CEngine::getstring()
{
  String Ret = "yee haw";
  return Ret;
}

CStatus CEngine::multiply(COperator &anOp)
{
 	CData* lData = &anOp.Left_Var.GetWorkingData();
  CData* rData = &anOp.Right_Var.GetWorkingData();
	 
	anOp.Result.SetType(lData->GetType());
	CData* res = &anOp.Result.GetWorkingData();

	*res = *lData;
	if(!res->operator_multiply(rData)){
	 return STAT_FAIL;
	}
	return STAT_SUCCESS;
}

CStatus CEngine::modulous(COperator &anOp)
{
 	CData* lData = &anOp.Left_Var.GetWorkingData();
  CData* rData = &anOp.Right_Var.GetWorkingData();
	
  if(rData->boolean()){
  }else{
    MAKEERR0(ERR03341)
  }

	anOp.Result.SetType(lData->GetType());
	CData* res = &anOp.Result.GetWorkingData();

	*res = *lData;
	if(!res->operator_modulous(rData)){
	 return STAT_FAIL;
	}
	return STAT_SUCCESS;
}


CStatus CEngine::power(COperator &anOp)
{

	CData* lData = &anOp.Left_Var.GetWorkingData();
  CData* rData = &anOp.Right_Var.GetWorkingData();
	 
	anOp.Result.SetType(lData->GetType());
	CData* res = &anOp.Result.GetWorkingData();

	*res = *lData;
	if(!res->operator_power(rData)){
	 return STAT_FAIL;
	}
	return STAT_SUCCESS;

  //SSI_INT64 lType = anOp.Left_Var.GetType();
  //SSI_INT64 rType = anOp.Right_Var.GetType();
  //SSI_INT64 w_exp = 0;
  //SSI_INT64 p_exp = 0;
  //SSI_INT64 ctr = 0;
  //SSI_INT64 iData = 0;
  //SSI_INT64 rData = 0;

  //if(lType == rType)
  //{
    /*
    if(lType == dINT)
    {
      CDataInt *lInt = (CDataInt*)anOp.Left_Var.firstData;
      CDataInt *rInt = (CDataInt*)anOp.Right_Var.firstData;
      w_exp = rInt->Data;
      iData = lInt->Data;
      
      if(w_exp == 0)
      {
        iData = 1;
      }
      else
      {
        for(ctr = 1; ctr < w_exp; ctr++)
        {
        //if(w_exp < 0)
        //{
        //  iData = iData / lInt->Data;
        //}
        //else
        //{
          iData = iData * lInt->Data;
        //}
        }
      }
      lInt->Data = iData;
      anOp.Result.Type = dINT;
      anOp.Result.SetData(lInt);
      return STAT_SUCCESS;
    }
    if(lType == dREAL)
    {
      //CDataReal *lReal = (CDataReal*)anOp.Left_Var.firstData;
      //CDataReal *rReal = (CDataReal*)anOp.Right_Var.firstData;
      //lReal->Data = lReal->Data rReal->Data;
      //anOp.Result.Type = dREAL;
      //anOp.Result.SetData(lReal);
      return STAT_SUCCESS;
    }
    */
  //}
  //return STAT_FAIL;
}

CStatus CEngine::divide(COperator &anOp)
{
	CData* lData = &anOp.Left_Var.GetWorkingData();
  CData* rData = &anOp.Right_Var.GetWorkingData();
	 
	anOp.Result.SetType(lData->GetType());
	CData* res = &anOp.Result.GetWorkingData();

  if(rData->boolean()){
  }else{
    MAKEERR0(ERR03342)
  }


	*res = *lData;
	if(!res->operator_divide(rData)){
	 return STAT_FAIL;
	}
	return STAT_SUCCESS;
}

CStatus CEngine::subtract(COperator &anOp)
{
	CData* lData = &anOp.Left_Var.GetWorkingData();
  CData* rData = &anOp.Right_Var.GetWorkingData();
	 
	anOp.Result.SetType(lData->GetType());
	CData* res = &anOp.Result.GetWorkingData();

	*res = *lData;
	if(!res->operator_minus(rData)){
	 return STAT_FAIL;
	}
	return STAT_SUCCESS;
}

CStatus CEngine::bit_and(COperator &anOp)
{
	CData* lData = &anOp.Left_Var.GetWorkingData();
  CData* rData = &anOp.Right_Var.GetWorkingData();
	 
	anOp.Result.SetType(lData->GetType());
	CData* res = &anOp.Result.GetWorkingData();

	*res = *lData;
	if(!res->operator_bit_and(rData)){
	 return STAT_FAIL;
	}
	return STAT_SUCCESS;
}

CStatus CEngine::bit_or(COperator &anOp)
{
	CData* lData = &anOp.Left_Var.GetWorkingData();
  CData* rData = &anOp.Right_Var.GetWorkingData();
	 
	anOp.Result.SetType(lData->GetType());
	CData* res = &anOp.Result.GetWorkingData();

	*res = *lData;
	if(!res->operator_bit_or(rData)){
	 return STAT_FAIL;
	}
	return STAT_SUCCESS;
}

CStatus CEngine::log_and(COperator &anOp)
{
		CDataBool Res;
    CData& Left = anOp.Left_Var.GetWorkingData();
	  CData& Right = anOp.Right_Var.GetWorkingData();
    Res.Data = Left.boolean() && Right.boolean();
		anOp.Result.SetType(CDataBool::GetRegisteredType());
    anOp.Result.SetWorkingData(&Res);
    return STAT_SUCCESS;
}

CStatus CEngine::log_or(COperator &anOp)
{
		CDataBool Res;
    CData& Left = anOp.Left_Var.GetWorkingData();
	  CData& Right = anOp.Right_Var.GetWorkingData();
    Res.Data = Left.boolean() || Right.boolean();
		anOp.Result.SetType(CDataBool::GetRegisteredType());
    anOp.Result.SetWorkingData(&Res);
    return STAT_SUCCESS;
}

SSI_INT64* CEngine::getidentity(SSI_INT64& Size)
{
  SSI_INT64 *ident = new SSI_INT64[9];
  ident[0] = 1;
  ident[1] = 0;
  ident[2] = 0;
  ident[3] = 0;
  ident[4] = 1;
  ident[5] = 0;
  ident[6] = 0;
  ident[7] = 0;
  ident[8] = 1;
  Size = 9;
  return ident;
}


// MUST BE REDONE
CStatus CEngine::compare(COperator &anOp)
{
	CStatus RetStat;
	CData& Left = anOp.Left_Var.GetWorkingData();
	CData& Right = anOp.Right_Var.GetWorkingData();

	if(!&Left || !&Right){
		return STAT_ERR;   // Internal Error
	}else{
		CDataBool Res; //= new CDataBool;
		SSI_INT64 Result;
		RetStat = Left.operator_compare(&Right, Result);
		if(!RetStat) return RetStat;

		if(Result == 0){  // EQUAL
			if(anOp.Type & oEQUAL){
				Res.Data = 1;
			}else if(anOp.Type == oEQUAL_EQUAL){
				Res.Data = 1;
			}else{
				Res.Data = 0;
			}
		}else if(Result > 0){  // GREATER
			if(anOp.Type & oGREATER){
				Res.Data = 1;
			}else if(anOp.Type == oNOT_EQUAL){
				Res.Data = 1;
			}else if(anOp.Type == oGREATER){
				Res.Data = 1;
			}else{
				Res.Data = 0;
			}
		}else if(Result < 0){  // GREATER
			if(anOp.Type & oLESS){
				Res.Data = 1;
			}else if(anOp.Type == oNOT_EQUAL){
				Res.Data = 1;
			}else if(anOp.Type == oLESS){
				Res.Data = 1;
			}else{
				Res.Data = 0;
			}
		}
		
		anOp.Result.SetType(CDataBool::GetRegisteredType());
    anOp.Result.SetWorkingData(&Res);
    return STAT_SUCCESS;
	}

  //SSI_INT64 lType = anOp.Left_Var.GetType();
  //SSI_INT64 rType = anOp.Right_Var.GetType();

	/*
	// 1 SSI_DISPLAY_FILE_NEW
  CDataBool* Res = new CDataBool;

  if(lType == rType)
  {
    if(lType == CDataString::GetRegisteredType())
    {
		  // This should be changed later
      return STAT_ERR;      
    }
    if(lType == CDataInt::GetRegisteredType())
    {
      CDataInt *lInt = (CDataInt*)&anOp.Left_Var.GetWorkingData();
      CDataInt *rInt = (CDataInt*)&anOp.Right_Var.GetWorkingData();

      if(anOp.Type & oLESS)
      {
        if(anOp.Type & oEQUAL)
        {
          if(lInt->Data <= rInt->Data) Res->Data = 1;
        }
        else
        {
          if(lInt->Data < rInt->Data) Res->Data = 1;
        }
      }
      else if(anOp.Type & oGREATER)
      {
        if(anOp.Type & oEQUAL)
        {
          if(lInt->Data >= rInt->Data) Res->Data = 1;
        }
        else
        {
          if(lInt->Data > rInt->Data) Res->Data = 1;
        }
      }
      else
      {
      }

      anOp.Result.SetType(CDataBool::GetRegisteredType());
      anOp.Result.SetWorkingData(Res);
      return STAT_SUCCESS;
    }
    if(lType == CDataReal::GetRegisteredType())
    {
      CDataReal *lReal = (CDataReal*)&anOp.Left_Var.GetWorkingData();
      CDataReal *rReal = (CDataReal*)&anOp.Right_Var.GetWorkingData();
      
      if(anOp.Type & oLESS)
      {
        if(anOp.Type & oEQUAL)
        {
          if(lReal->Data <= rReal->Data) Res->Data = 1;
        }
        else
        {
          if(lReal->Data < rReal->Data) Res->Data = 1;
        }
      }
      else if(anOp.Type & oGREATER)
      {
        if(anOp.Type & oEQUAL)
        {
          if(lReal->Data >= rReal->Data) Res->Data = 1;
        }
        else
        {
          if(lReal->Data > rReal->Data) Res->Data = 1;
        }
      }
      else
      {
      }
      
      anOp.Result.SetType(CDataBool::GetRegisteredType());
      anOp.Result.SetWorkingData(Res);
      return STAT_SUCCESS;
    }
    
  }
  return STAT_FAIL;
	*/
}

void CEngine::disp(CData *InData)
{
  if(!InData) return;
  String Str;
  Str = InData->GetString();
	if(out){
		out->Disp(Str, 1);  
	}
}

CEngine* CEngine::CreateFunctionEngine()
{
	// 1 SSI_DISPLAY_FILE_NEW
  CEngine* anEngine = new CEngine(0); // 0 to not add local functions
  anEngine->SetInterpreter(Interpreter);

	anEngine->out = out;
  anEngine->SetParent(this);

  if(anEngine->FuncSet){
    delete anEngine->FuncSet;
  }
  anEngine->FuncSet = FuncSet;

	
  if(anEngine->Environment){
    delete anEngine->Environment;    
  }
  anEngine->Environment = Environment;

  return anEngine;
}

void CEngine::SetParent(CEngine *Par)
{
  parEngine = Par;
}

CInterpreter* CEngine::GetInterpreter(void)
{
	return Interpreter;
}

void CEngine::SetOutDisplay(ssi_stream* NewDisp)
{
  out = NewDisp;
}

void CEngine::what(void)
{

	StringNode* aNode = FuncSet->Packages.getTop();
	String s = "  ";
  String temp = "  ";
  int icnt = 0;

  if(CDataFactory::getInstance()){
    out->Disp("Available Data Types:",1);
    RegNode aNode = CDataFactory::getInstance()->FirstReg;
    while(aNode){
      if(aNode->aData){
        icnt = icnt + 1;
        s = s + aNode->aData->DataName + ", ";
        aNode = aNode->Next;
        if(icnt >= 5){
          s.chop();  // Remove last comma and space from line
          s.chop();
          out->Disp(s.Str(), 1);
          s = "  ";
          icnt = 0;
        }
      }
    }
    String s2 = s;
    s2.Trim();
    if(s2.GetLength() > 0){
      s.chop();
      s.chop();
      out->Disp(s.Str(), 1);
    }
    out->Disp("",1);
  }

	out->Disp("Available Packages:",1);
	while(aNode){
		aNode->Get(s);
    s = temp + s;
		aNode = aNode->next();
		out->Disp(s.Str(), 1);
	}

	/*
	CFunctionDecl aDecl;
	FuncNode* topNode = FuncSet.Funcs.getTop();//Funcs.getTop();
	//String sDisp;
	String sParams;
	SSI_INT64 NumParams;
	SSI_INT64 ctr = 0;
	SSI_INT64 Type = 0;

	SSI_CHAR StrBuff[68];
  CSSI_CHAR* fmt = "%-25.24s%-41.41s";

  sprintf(StrBuff, fmt, "Function Names", "Parameters");
	out->Disp(StrBuff, 1);
  out->Disp("------------------------------------------------------------------------------", 1);
	while(topNode){
		topNode->Get(aDecl);
		//sDisp = aDecl.Name;
		NumParams = aDecl.getNumParams();
		for(ctr = 0; ctr < NumParams; ctr++){
			Type = aDecl.getParamType(ctr);
			
			CData* aData = Factory_MakeData(Type);
			if(aData){
				sParams = sParams + aData->DataName + ",";
				delete aData;
			}
		}
		sParams.chop();
		
		sprintf(StrBuff, fmt, (CSSI_CHAR*)aDecl.Name, (CSSI_CHAR*)sParams);
	  out->Disp(StrBuff, 1);

		//sDisp = sDisp + "\t\t\t";
		//sDisp = sDisp + sParams;
		//out->Disp(sDisp, 1);
		//sDisp = "";
		sParams = "";
		topNode = topNode->next();		
	}
	*/
}

void CEngine::what(String& Package)
{
	CFunctionDecl aDecl;
	FuncNode* topNode = FuncSet->Funcs.getTop();//Funcs.getTop();
  //FuncSet.Funcs.sort();  Not implemented

	String sParams;
	String sDisp;
	SSI_INT64 NumParams;
	SSI_INT64 ctr = 0;
	SSI_INT64 Type = 0;

	SSI_CHAR StrBuff[68];
  CSSI_CHAR* fmt = "%-25.24s%-12.11s%-30.30s";

	if(!FuncSet->Packages.find(Package)){
		sDisp.Format("Package '%s' not found", Package.Str());
		out->Disp(sDisp.Str(), 1);
		return;
	}

	sDisp.Format("Package '%s'", Package.Str());
	out->Disp(sDisp.Str(), 1);

  sprintf(StrBuff, fmt, "Function Names", "Return", "Parameters");
	out->Disp(StrBuff, 1);
  out->Disp("------------------------------------------------------------------------------", 1);
	while(topNode){
		topNode->Get(aDecl);

		if(aDecl.Package == Package){

			NumParams = aDecl.getNumParams();
			for(ctr = 0; ctr < NumParams; ctr++){
				Type = aDecl.getParamType(ctr);
			
				CData* aData = Factory_MakeData(Type);
				if(aData){
					sParams = sParams + aData->DataName + ",";
					delete aData;
				}else if(Type == dANY){
				  sParams = sParams + "any,";
			  }
			}
			sParams.chop();

			CData* aData = Factory_MakeData(aDecl.ReturnType);
			String sReturn;
			if(aData){
				sReturn = aData->DataName;
				delete aData;
			}else if(aDecl.ReturnType == dANY){
				sReturn = "any";
			}
			sprintf(StrBuff, fmt, (CSSI_CHAR*)aDecl.Name, (CSSI_CHAR*)sReturn, (CSSI_CHAR*)sParams);
			out->Disp(StrBuff, 1);
		}
		sParams = "";
		topNode = topNode->next();		
	}
}

void CEngine::whatHELP(String& Package, String& FileName)
{

	FILE* fh = fopen(FileName.Str(), "w");
	if(!fh){
		return;
	}

	fprintf(fh, "<HTML><HEAD><TITLE>Phantom v2.0 Beta Functions (%s Package)</TITLE></HEAD>\n<BODY>\n<br><h1>Phantom v2.0 Beta Functions (%s Package)</h1><p>\n", Package.Str(), Package.Str());
	fprintf(fh, "<hr>\n");

	CFunctionDecl aDecl;
	FuncNode* topNode = FuncSet->Funcs.getTop();

	String sParams;
	String sDisp;
	SSI_INT64 NumParams;
	SSI_INT64 ctr = 0;
	SSI_INT64 Type = 0;

	if(!FuncSet->Packages.find(Package)){
		sDisp.Format("Package '%s' not found", Package.Str());
		out->Disp(sDisp.Str(), 1);
		return;
	}

	while(topNode){
		topNode->Get(aDecl);

		if(aDecl.Package == Package){

			NumParams = aDecl.getNumParams();
			for(ctr = 0; ctr < NumParams; ctr++){
				Type = aDecl.getParamType(ctr);
			
				CData* aData = Factory_MakeData(Type);
				if(aData){
					sParams = sParams + aData->DataName + ",";
					delete aData;
				}
			}
			sParams.chop();

			CData* aData = Factory_MakeData(aDecl.ReturnType);
			String sReturn;
			if(aData){
				sReturn = aData->DataName;
				delete aData;
			}
			String sTemp1 = sReturn;
			if(sReturn.GetLength() <= 0){
				sTemp1 = "none";
			}
			String sTemp2 = sParams;
			if(sParams.GetLength() <= 0){
				sTemp2 = "none";
			}

			fprintf(fh, "<h2><b>%s</b></h2><p>\n", aDecl.Name.Str());
			fprintf(fh, "<b>Syntax:</b><br>\n<i>%s %s(%s)</i><p>\n", sReturn.Str(), aDecl.Name.Str(), sParams.Str());
			fprintf(fh, "<b>Return Type:</b> %s<br>\n", sTemp1.Str());
			fprintf(fh, "<b>Parameters:</b> %s<br>\n", sTemp2.Str());
			fprintf(fh, "<b>Description:</b> Coming soon...<p>\n");
			fprintf(fh, "<hr>\n");
		}
		sParams = "";
		topNode = topNode->next();		
	}
	fprintf(fh, "\n\n</BODY></HTML>");
	fclose(fh);
}

CStatus CEngine::AddKeyword(CKeywordDecl& aKey)
{
	KeyNode* aNode = Keys.find(aKey);
	if(aNode){
		// Keyword already exists
		MAKEERR1(ERR03300, aKey.m_Keyword.Str());
	}
	Keys.push(aKey);
	return STAT_OK;
}

CStatus CEngine::DoKeyword(CKeywordDecl& aKey, String& InData)
{
	KeyNode* aNode = Keys.find(aKey);
	if(aNode){
		aNode->Get(aKey);
		SSI_KeywordAPI theAPI;
		theAPI.m_Keyword = aKey.m_Keyword;
		theAPI.m_Engine = this;
		theAPI.m_Data = InData;

		// Get rid of any line end character
		Interpreter->ChopEnder(theAPI.m_Data);

		//if(!ChopEnder(In)){
			//MAKEERR1(ERR03100, STATEMENT_ENDER);
		//}

		if(!aKey.theProcessor){
			return STAT_ERR;   // Internal Error
		}else{
			aKey.theProcessor(theAPI);
			if(theAPI.m_Status){
				return STAT_SUCCESS;
			}else{
				return theAPI.m_Status;
			}
		}
		return STAT_OK;
	}
	return STAT_OK;
}

CStatus CEngine::AddVariable(CData& InData, String& Name)
{
	if(!&InData) return STAT_ERR;  // Internal Error (Dev?)
	
	CVariable aVar;
	aVar.Name = Name;
	aVar.SetType(InData.Type);
	aVar.SetData(&InData);
	return AddVariable(aVar);
}

ssi_stream* CEngine::GetOutStream(void)
{
	return out;
}

CStatus CEngine::SetPackageName(const char* Name)
{
	return FuncSet->SetPackageName(Name);
}

CStatus CEngine::increment(COperator& anOp)
{
	CData* lData = &anOp.Left_Var.GetWorkingData();
	anOp.Result.SetType(lData->GetType());
	anOp.Result.Name = anOp.Left_Var.Name;
	CData* res = &anOp.Result.GetWorkingData();

	*res = *lData;
	CStatus RetStat;
	RetStat = res->operator_increment();
	return RetStat;
}

CStatus CEngine::decrement(COperator& anOp)
{
	CData* lData = &anOp.Left_Var.GetWorkingData();
	anOp.Result.SetType(lData->GetType());
	anOp.Result.Name = anOp.Left_Var.Name;
	CData* res = &anOp.Result.GetWorkingData();

	*res = *lData;
	CStatus RetStat;
	RetStat = res->operator_decrement();
	return RetStat;
}

void CEngine::ClearVariables(void)
{
  Vars.clear();
}

void CEngine::RemoveVariable(CVariable& Var)
{
  VarItor Itor;

  //VarTree V;
  //V.remove(Itor);


  Itor = find(Vars.begin(), Vars.end(), Var);  
	if(Itor != Vars.end())
  {
    Var = *Itor;
    Vars.remove(Var);
//    Vars.erase(Itor, Itor);
  }  
}

void CEngine::RemoveFunction(String& Name)
{
  //CFunctionSet F;
  CFunctionDecl aDecl;
  aDecl.Name = Name;
  //F.FindFunctionDecl(

	FuncNode* aFuncPtr = SSI_NULL;
	while(aFuncPtr = FuncSet->FindFunctionDecl(aDecl, aFuncPtr)){
    //FuncSet.Funcs.clear(aFuncPtr);
    FuncSet->Funcs.clear_range(aFuncPtr, aFuncPtr);
    aFuncPtr = SSI_NULL;
  }


}

void CEngine::ClearFunctions(void)
{
  //FuncSet.Funcs.Clear();
  FuncSet->ClearPackage(String("User Defined"));
}

/*
int CEngine::StartLog(String& FileName)
{
  if(parEngine){
    return parEngine->StartLog(FileName);
  }
  if(log){
    delete log;  // deletion closes
  }
  log = new File();
  if(!log->Open(FileName, "w")){
    delete log;
    log = SSI_NULL;
    return 0;
  }else{
    log->WriteLine(FileName + "\n");
    return 1;
  }
}
*/

CStatus CEngine::Log(String& Message)
{
  out->LogDisp(Message, 1);
  return STAT_OK;
}
void CEngine::SetLoopEngine(void)
{
  IsLoopEngine = 1;
}

String CEngine::GetVariableTypes(const String& Delim){
  String Ret;
  if(CDataFactory::getInstance()){
    RegNode aNode = CDataFactory::getInstance()->FirstReg;
    while(aNode){
      if(aNode->aData){
        if(aNode->Next){
          Ret = Ret + aNode->aData->DataName + Delim;
        }else{
          Ret = Ret + aNode->aData->DataName;
        }
        aNode = aNode->Next;
      }
    }
  }
  return Ret;
}

String CEngine::GetPackageContents(const String& PackDelim, const String& FuncDelim){
  String Ret;

	CFunctionDecl aDecl;

  StringNode* aNode = FuncSet->Packages.getTop();
  int PackCount = 0;
	while(aNode){
    PackCount++;
    aNode = aNode->next();
  }

  if(PackCount <= 0) return "";

  if(PackCount > 255) PackCount = 255;

  String* s = new String[PackCount];
  String* r = new String[PackCount];
  //String s[255]; //[PackCount];
  //String r[255]; //[PackCount];
  aNode = FuncSet->Packages.getTop();
  PackCount = 0;
  while(aNode){
    aNode->Get(s[PackCount]);
    r[PackCount] = s[PackCount];
		aNode = aNode->next();
    PackCount++;
	}

	FuncNode* topNode = FuncSet->Funcs.getTop();//Funcs.getTop();

  String sParams;
  String sFunction;
  int NumParams;

  while(topNode){
		topNode->Get(aDecl);

    for(int i = 0; i < PackCount; i++){
      if(aDecl.Package == s[i]){
        sParams = "";
        NumParams = aDecl.getNumParams();
			  for(int ctr = 0; ctr < NumParams; ctr++){
				  SSI_INT64 Type = aDecl.getParamType(ctr);			
				  CData* aData = Factory_MakeData(Type);
				  if(aData){
					  sParams = sParams + aData->DataName + ",";
					  delete aData;
				  }else if(Type == dANY){
				    sParams = sParams + "any,";
			    }
			  }
   			sParams.chop();

  			CData* aData = Factory_MakeData(aDecl.ReturnType);
	  		String sReturn;
		  	if(aData){
  				sReturn = aData->DataName;
	  			delete aData;
  			}else if(aDecl.ReturnType == dANY){
	  			sReturn = "any";
  			}

        sFunction = sReturn + " " + aDecl.Name + "(" + sParams + ")";
        r[i] = r[i] + FuncDelim + sFunction;
        i = PackCount + 1;
      }
    }

    topNode = topNode->next();
  }	
  Ret = "";
  for(int i = 0; i < PackCount; i++){
    if(i < PackCount - 1){
      Ret = Ret + r[i] + PackDelim;
    }else{
      Ret = Ret + r[i];
    }
  }

  delete [] s;
  delete [] r;

  return Ret;
}

String CEngine::GetAllVariableMembers(const String& VarDelim, const String& FuncDelim){
  String Ret;

  if(CDataFactory::getInstance()){
    RegNode aNode = CDataFactory::getInstance()->FirstReg;
    while(aNode){
      if(aNode->aData){

        Ret = Ret + aNode->aData->DataName + FuncDelim;
        Ret = Ret + aNode->aData->GetMemberFunctions(FuncDelim);

        if(aNode->Next){
          Ret = Ret + VarDelim;
        }
        aNode = aNode->Next;
      }
    }
  }
  return Ret;
}

CStatus CEngine::GetKeywordStringInput(String& inValue)
{
  CStatus Ret;
  CVariable Var;
  if(Ret = Interpreter->Evaluate(inValue, Var)){
    if(Var.GetType() != CDataString::GetRegisteredType()){
      MAKEERR0(ERR03343);
    }else{
      CDataString* S = (CDataString*)&Var.GetWorkingData();
      inValue = S->Data;
      return STAT_OK;
    }
  }
  return Ret;
}
